﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;

namespace NetTopologySuite.Noding.Snapround
{
	public class MCIndexSnapRounder : INoder
	{
		public MCIndexSnapRounder(IPrecisionModel pm)
		{
			this._li = new RobustLineIntersector
			{
				PrecisionModel = pm
			};
			this._scaleFactor = pm.Scale;
		}

		public IList<ISegmentString> GetNodedSubstrings()
		{
			return NodedSegmentString.GetNodedSubstrings(this._nodedSegStrings);
		}

		public void ComputeNodes(IList<ISegmentString> inputSegmentStrings)
		{
			this._nodedSegStrings = inputSegmentStrings;
			this._noder = new MCIndexNoder();
			this._pointSnapper = new MCIndexPointSnapper(this._noder.Index);
			this.SnapRound(inputSegmentStrings, this._li);
		}

		private void SnapRound(IList<ISegmentString> segStrings, LineIntersector li)
		{
			IList<Coordinate> snapPts = this.FindInteriorIntersections(segStrings, li);
			this.ComputeIntersectionSnaps(snapPts);
			this.ComputeVertexSnaps(segStrings);
		}

		private IList<Coordinate> FindInteriorIntersections(IList<ISegmentString> segStrings, LineIntersector li)
		{
			InteriorIntersectionFinderAdder interiorIntersectionFinderAdder = new InteriorIntersectionFinderAdder(li);
			this._noder.SegmentIntersector = interiorIntersectionFinderAdder;
			this._noder.ComputeNodes(segStrings);
			return interiorIntersectionFinderAdder.InteriorIntersections;
		}

		private void ComputeIntersectionSnaps(IEnumerable<Coordinate> snapPts)
		{
			foreach (Coordinate pt in snapPts)
			{
				HotPixel hotPixel = new HotPixel(pt, this._scaleFactor, this._li);
				this._pointSnapper.Snap(hotPixel);
			}
		}

		public void ComputeVertexSnaps(IList<ISegmentString> edges)
		{
			foreach (ISegmentString segmentString in edges)
			{
				INodableSegmentString e = (INodableSegmentString)segmentString;
				this.ComputeVertexSnaps(e);
			}
		}

		private void ComputeVertexSnaps(INodableSegmentString e)
		{
			Coordinate[] coordinates = e.Coordinates;
			for (int i = 0; i < coordinates.Length; i++)
			{
				HotPixel hotPixel = new HotPixel(coordinates[i], this._scaleFactor, this._li);
				if (this._pointSnapper.Snap(hotPixel, e, i))
				{
					e.AddIntersection(coordinates[i], i);
				}
			}
		}

		private readonly LineIntersector _li;

		private readonly double _scaleFactor;

		private MCIndexNoder _noder;

		private MCIndexPointSnapper _pointSnapper;

		private IList<ISegmentString> _nodedSegStrings;
	}
}
